﻿// OnvifInfoDialog.cpp: 实现文件
//

#include "pch.h"
#include "MFCFFMPEG.h"
#include "OnvifInfoDialog.h"
#include "afxdialogex.h"
#include <iostream>
#include <list>
#include <string>
#include <IPHlpApi.h>
#pragma comment(lib, "IPHlpApi.lib")
using namespace std;

int urldecode(const char* url, struct URLInfo* urlinfo)
{
	const char* ptr = url, * pos;
	const char* pend;
	int len = strlen(url);
	int flagauth = 0;

	ptr = strstr(url, "://");
	if (ptr == NULL)
		return -1;
	ptr += 3;
	strncpy(urlinfo->prefix, url, ptr - url);

	pend = strchr(ptr, '/');
	if (pend == NULL)
		pend = url + len;
	//寻找  username:password@host:port

	const char* pname_end = strchr(ptr, ':');
	const char* phost_begin;

	for (phost_begin = pend; phost_begin > ptr; phost_begin--)
	{
		if (*phost_begin == '@')
			break;
	}

	if (*phost_begin == '@' && pname_end < pend)
	{
		flagauth = 1;
		strncpy(urlinfo->useranme, ptr, pname_end - ptr);
		ptr = pname_end + 1;
		strncpy(urlinfo->password, ptr, phost_begin - ptr);
		ptr = phost_begin + 1;
	}
	//
	if (ptr >= pend)
	{
		printf("[%s]unknow host port\n", url);
		return -1;
	}
	//host and port;
	for (pos = ptr; pos < pend; pos++)
		if (*pos == ':')
			break;
	strncpy(urlinfo->host, ptr, pos - ptr);
	if (*pos == ':')
		ptr = pos + 1;
	else
		ptr = pos;

	if (ptr < pend)
	{
		urlinfo->port = atoi(ptr);
		ptr = pend;
	}
	else
	{
		if (strcmp(urlinfo->prefix, "http://") == 0)
			urlinfo->port = 80;
		if (strcmp(urlinfo->prefix, "https://") == 0)
			urlinfo->port = 443;
	}
	urlinfo->path = ptr;

	return 0;
}

BOOL GetLocalAdaptersInfo(list<string> &ips)
{
	//IP_ADAPTER_INFO结构体
	PIP_ADAPTER_INFO pIpAdapterInfo = NULL;
	pIpAdapterInfo = new IP_ADAPTER_INFO;

	//结构体大小
	unsigned long ulSize = sizeof(IP_ADAPTER_INFO);

	//获取适配器信息
	int nRet = GetAdaptersInfo(pIpAdapterInfo, &ulSize);

	if (ERROR_BUFFER_OVERFLOW == nRet)
	{
		//如果函数返回的是ERROR_BUFFER_OVERFLOW
	   //则说明GetAdaptersInfo参数传递的内存空间不够,同时其传出stSize,表示需要的空间大小
	   //这也是说明为什么stSize既是一个输入量也是一个输出量
	   //释放原来的内存空间

		//空间不足，删除之前分配的空间
		delete[] pIpAdapterInfo;

		//重新分配大小
		pIpAdapterInfo = (PIP_ADAPTER_INFO) new BYTE[ulSize];

		//获取适配器信息
		nRet = GetAdaptersInfo(pIpAdapterInfo, &ulSize);

		//获取失败
		if (ERROR_SUCCESS != nRet)
		{
			if (pIpAdapterInfo != NULL)
			{
				delete[] pIpAdapterInfo;
			}
			return FALSE;
		}
	}

	//MAC 地址信息
	char szMacAddr[20];
	//赋值指针
	PIP_ADAPTER_INFO pIterater = pIpAdapterInfo;
	while (pIterater)
	{
		cout << "网卡名称：" << pIterater->AdapterName << endl;
		cout << "网卡描述：" << pIterater->Description << endl;

		sprintf_s(szMacAddr, 20, "%02X-%02X-%02X-%02X-%02X-%02X",
			pIterater->Address[0],
			pIterater->Address[1],
			pIterater->Address[2],
			pIterater->Address[3],
			pIterater->Address[4],
			pIterater->Address[5]);

		cout << "MAC 地址：" << szMacAddr << endl;
		cout << "IP地址列表：" << endl << endl;

		//指向IP地址列表
		PIP_ADDR_STRING pIpAddr = &pIterater->IpAddressList;
		while (pIpAddr)
		{
			cout << "IP地址：  " << pIpAddr->IpAddress.String << endl;
			cout << "子网掩码：" << pIpAddr->IpMask.String << endl;

			ips.push_back(pIpAddr->IpAddress.String);

			//指向网关列表
			PIP_ADDR_STRING pGateAwayList = &pIterater->GatewayList;
			while (pGateAwayList)
			{
				cout << "网关：    " << pGateAwayList->IpAddress.String << endl;
				pGateAwayList = pGateAwayList->Next;
			}

			pIpAddr = pIpAddr->Next;
		}

		pIterater = pIterater->Next;
	}

	//清理
	if (pIpAdapterInfo)
		delete[] pIpAdapterInfo;
	return TRUE;
}

// OnvifInfoDialog 对话框

#define WM_ONVIF_INFO_MSG WM_USER+1

IMPLEMENT_DYNAMIC(OnvifInfoDialog, CDialogEx)

OnvifInfoDialog::OnvifInfoDialog(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG_ONVIF_INFO, pParent)
	, m_devURI(_T(""))
	, m_UserName(_T(""))
	, m_Password(_T(""))
	, m_onvifInfoText(_T(""))
	, m_OneOnvifIP(FALSE)
{
	_onvifDevice = NULL;
}

OnvifInfoDialog::~OnvifInfoDialog()
{
	if (_onvifDevice)
		delete _onvifDevice;
}

void OnvifInfoDialog::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_DEVURI, m_devURI);
	DDX_Text(pDX, IDC_EDIT_USERNAME, m_UserName);
	DDX_Text(pDX, IDC_EDIT_PASSWORD, m_Password);
	DDX_Text(pDX, IDC_EDIT_INFO, m_onvifInfoText);
	DDX_Control(pDX, IDC_COMBO_PLAYURL, m_BoxURL);
	DDX_Check(pDX, IDC_CHECK_ONE_ONVIF_IP, m_OneOnvifIP);
	DDX_Control(pDX, IDC_COMBO_NET_IP, m_ComBoxNetIP);
	DDX_Control(pDX, IDC_LIST_ONVIF_DEVICE, m_uiListOnvif);
}


BEGIN_MESSAGE_MAP(OnvifInfoDialog, CDialogEx)
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_BUTTON_DEVFIND, &OnvifInfoDialog::OnBnClickedButtonDevfind)
	ON_MESSAGE(WM_ONVIF_INFO_MSG, &OnvifInfoDialog::OnOnvifInfoMsg)
	ON_BN_CLICKED(IDC_BUTTON_PLAY, &OnvifInfoDialog::OnBnClickedButtonPlay)
	ON_BN_CLICKED(IDC_BUTTON_ONVIF_SEARCH, &OnvifInfoDialog::OnBnClickedButtonOnvifSearch)
	ON_NOTIFY(NM_DBLCLK, IDC_LIST_ONVIF_DEVICE, &OnvifInfoDialog::OnNMDblclkListOnvifDevice)
END_MESSAGE_MAP()

BOOL OnvifInfoDialog::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO:  在此添加额外的初始化

	CRect m_rect;
	GetClientRect(&m_rect);             // 获取对话框的矩形区域
	m_rect.top = m_rect.bottom - 20;    // 设置状态栏的矩形区域
	if (m_statusBar.Create(WS_BORDER | WS_VISIBLE | CBRS_BOTTOM, m_rect, this, 3))
	{
		int nParts[4] = { 100,  -1 };  // 分割尺寸
		m_statusBar.SetParts(2, nParts);    // 分割状态栏
		m_statusBar.SetText(L"Onvif", 0, 0);    // 第一个分栏
		m_statusBar.SetText(L"...", 1, 0);     // 第二个
		m_statusBar.ShowWindow(SW_SHOW);
	}	

	//本机IP
	m_ComBoxNetIP.ResetContent();
	list<string> ips;
	GetLocalAdaptersInfo(ips);	
	list<string>::iterator itr;
	for (itr = ips.begin(); itr != ips.end(); itr++)
	{
		USES_CONVERSION;
		CString ipaddr = A2T((*itr).c_str());
		m_ComBoxNetIP.AddString(ipaddr);
	}
	if (ips.size() > 0) {
		m_ComBoxNetIP.SetCurSel(0);
	}

	m_uiListOnvif.InsertColumn(0, L"IP地址",0, 100);
	m_uiListOnvif.InsertColumn(1, L"XAddress", 0, 200);

	//
	SetDlgItemText(IDC_EDIT_DEVURI, L"http://192.168.0.150:80/onvif/device_service");
	SetDlgItemText(IDC_EDIT_USERNAME, L"admin");
	SetDlgItemText(IDC_EDIT_PASSWORD, L"admin");
	
	_onvifDevice = NULL;

	return TRUE;  // return TRUE unless you set the focus to a control
				  // 异常: OCX 属性页应返回 FALSE
}

// OnvifInfoDialog 消息处理程序
void OnvifInfoDialog::OnClose()
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	CDialogEx::OnClose();
	DestroyWindow();
}


void OnvifInfoDialog::PostNcDestroy()
{
	// TODO: 在此添加专用代码和/或调用基类
	CDialogEx::PostNcDestroy();
	delete this;
}


static DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	OnvifDevice* info = (OnvifDevice*)lpParam;
	int ret;

	ret = info->GetDeviceInformation();
	if (ret == 200) {
		ret = info->GetCapabilities_Media();
	}
	
	if (ret == 200) {
		ret = info->MediaService_GetProfiles();
	}

	if (ret == 200) {
		ret = info->MediaServer_GetStreamUri();
	}
	
	::SendMessage(info->m_hWnd, WM_ONVIF_INFO_MSG, 0, 0);
	//delete info;
	return 0;
}

void OnvifInfoDialog::OnBnClickedButtonDevfind()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(TRUE);
	HANDLE hThread = NULL;

	USES_CONVERSION;

	char* uri = T2A(m_devURI.GetBuffer(0));
	char* username = T2A(m_UserName.GetBuffer(0));
	char* password = T2A(m_Password.GetBuffer(0));

	if (_onvifDevice != NULL)
		delete _onvifDevice;

	m_statusBar.SetText(L"Onvif", 0, 0);     // 第二个
	m_statusBar.SetText(L"搜索中", 1, 0);     // 第二个
	m_statusBar.ShowWindow(SW_SHOW);

	this->_onvifDevice = new OnvifDevice(this->m_hWnd, uri, username, password, this->m_OneOnvifIP);
	
	SetDlgItemText(IDC_EDIT_INFO, L"开始查找中\r\n");

	m_devURI.ReleaseBuffer(); 					//千万不能缺少
	m_UserName.ReleaseBuffer();
	m_Password.ReleaseBuffer();

	hThread = CreateThread(
		NULL,//default security attributes
		0,//use default stack size
		ThreadProc,//thread function
		_onvifDevice,//argument to thread function
		0,//use default creation flags
		NULL);//returns the thread identifier
	if (hThread != NULL)
		CloseHandle(hThread);

	m_BoxURL.ResetContent();
}


afx_msg LRESULT OnvifInfoDialog::OnOnvifInfoMsg(WPARAM wParam, LPARAM lParam)
{
	if (_onvifDevice == NULL)
		return 0;

	USES_CONVERSION;

	CString FirmwareVersion = A2T(_onvifDevice->devInfo.FirmwareVersion);
	CString Manufacturer = A2T(_onvifDevice->devInfo.Manufacturer);
	CString Model = A2T(_onvifDevice->devInfo.Model);
	CString SerialNumber = A2T(_onvifDevice->devInfo.SerialNumber);
	CString HardwareId = A2T(_onvifDevice->devInfo.HardwareId);

	CString msgc = L"";
	CString stat = A2T(_onvifDevice->getStatStr());

	msgc.Append(stat);
	if (_onvifDevice->devInfo.httpRespStatus == 200)
	{
		msgc.Append(L"固件版本:");
		msgc.Append(FirmwareVersion);
		msgc.Append(L"\r\n");

		msgc.Append(L"制造商:");
		msgc.Append(Manufacturer);
		msgc.Append(L"\r\n");

		msgc.Append(L"model:");
		msgc.Append(Model);
		msgc.Append(L"\r\n");

		msgc.Append(L"序列号:");
		msgc.Append(SerialNumber);
		msgc.Append(L"\r\n");

		msgc.Append(L"硬件ID:");
		msgc.Append(HardwareId);
		msgc.Append(L"\r\n");
	}	

	if (strlen(_onvifDevice->mediaUrl) > 0) {
		CString mediaUrl = A2T(_onvifDevice->mediaUrl);
		msgc.Append(L"媒体URL:");
		msgc.Append(mediaUrl);
		msgc.Append(L"\r\n");
		msgc.Append(L"\r\n");
	}
	
	if (_onvifDevice->profiles[0].httpRespStatus == 200)
	{
		for (int i = 0; i < 5; i++)
		{
			if (strlen(_onvifDevice->profiles[i].token) == 0)
				break;

			char msg[200];
			sprintf(msg,"token=%s,encode:%s,%dx%d", 
						_onvifDevice->profiles[i].token,
						_onvifDevice->profiles[i].encodname, 
						_onvifDevice->profiles[i].width,
						_onvifDevice->profiles[i].height
						);

			CString msgcs = A2T(msg);
			CString url = A2T(_onvifDevice->urls[i]);


			//IDC_COMBO_PLAYURL
			m_BoxURL.Clear();
			m_BoxURL.AddString(url);

			msgc.Append(L"-----");
			msgc.Append(msgcs);
			msgc.Append(L"-----\r\n");
			msgc.Append(url);
			msgc.Append(L"\r\n");
			msgc.Append(L"\r\n");
		}
	}

	//((CEdit*)GetDlgItem(IDC_EDIT_INFO))->SetSel(GetDlgItem(IDC_EDIT_INFO)->GetWindowTextLength(), GetDlgItem(IDC_EDIT_INFO)->GetWindowTextLength());
	//((CEdit*)GetDlgItem(IDC_EDIT_INFO))->ReplaceSel(msgc + L"\n");
	SetDlgItemText(IDC_EDIT_INFO, msgc);

	m_statusBar.SetText(L"Onvif", 0, 0);     // 第二个
	m_statusBar.SetText(L"搜索完成", 1, 0);     // 第二个
	m_statusBar.ShowWindow(SW_SHOW);
	return 0;
}


void OnvifInfoDialog::OnBnClickedButtonPlay()
{
	UpdateData(TRUE);

	int curSel = m_BoxURL.GetCurSel();
	CString url;
	m_BoxURL.GetWindowText(url);

	if (url.GetLength() == 0)
		return;

CString url2 = L"";

if (m_UserName.GetLength() > 0) {
	url2.Append(L"rtsp://");
	url2.Append(m_UserName);
	url2.Append(L":");
	url2.Append(m_Password);
	url2.Append(L"@");
	url.Delete(0, strlen("rtsp://"));
	url2.Append(url);
}
else {
	url2 = url;
}


TCHAR exeFullPath[MAX_PATH];
CString strPath;
GetModuleFileName(NULL, exeFullPath, MAX_PATH);
strPath = (CString)exeFullPath;
int position = strPath.ReverseFind('\\');
strPath = strPath.Left(position + 1);


TCHAR FilePath[MAX_PATH];
GetModuleFileName(NULL, FilePath, MAX_PATH);
(_tcsrchr(FilePath, '\\'))[1] = 0;//// 删除文件名，只获得路径字串   // C:\**\**\
   
{
	CString cmdLine;

	cmdLine.Append(FilePath);
	cmdLine.Append(L"ffplay.exe -x 640 -y 480  -max_delay 500000 -rtsp_transport tcp -i ");
	cmdLine.Append(url2);

	STARTUPINFO si = { sizeof(si) };
	PROCESS_INFORMATION pi;

	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = TRUE; //TRUE表示显示创建的进程的窗体

	LPWSTR cmd = cmdLine.GetBuffer(0);

	BOOL bRet = ::CreateProcess(
		NULL,
		cmd, //在Unicode版本号中此參数不能为常量字符串，由于此參数会被改动	 
		NULL,
		NULL,
		FALSE,
		CREATE_NEW_CONSOLE,
		NULL,
		NULL,
		&si,
		&pi);

	cmdLine.ReleaseBuffer();

	int error = GetLastError();
	if (bRet)
	{
		::CloseHandle(pi.hThread);
		::CloseHandle(pi.hProcess);

		printf(" 新进程的进程ID号：%d /n", pi.dwProcessId);
		printf(" 新进程的主线程ID号：%d /n", pi.dwThreadId);

		m_statusBar.SetText(L"播放", 0, 0);
		m_statusBar.SetText(cmdLine, 1, 0);
	}
	else
	{
		printf("error code:%d/n", error);

		m_statusBar.SetText(L"播放", 0, 0);
		m_statusBar.SetText(L"播放错误, 不能启动ffplay.exe", 1, 0);
	}
}
m_statusBar.ShowWindow(SW_SHOW);
}


class DiscoverInfo {
public:
	string ipaddr;
	struct OnvifDeviceProbeMatch probe;
};

static void _outonvifip(void* context, const char* ipaddress, uint32_t ser_addr, struct OnvifDeviceProbeMatch* probe)
{	
	list<DiscoverInfo>* infos = (list<DiscoverInfo>*)context;
	list<DiscoverInfo>::iterator itr;

	for (itr = infos->begin(); itr != infos->end(); itr++)
	{
		if (strcmp( (*itr).ipaddr.c_str(), ipaddress) == 0)
		{
			return;
		}
	}

	DiscoverInfo info;
	info.ipaddr = ipaddress;
	info.probe = *probe;
	infos->push_back(info);
}

void OnvifInfoDialog::OnBnClickedButtonOnvifSearch()
{
	// TODO: 在此添加控件通知处理程序代码
	CString ipaddr;
	m_ComBoxNetIP.GetWindowText(ipaddr);
	if (ipaddr.GetLength() == 0)
		return;

	USES_CONVERSION;
	char* mip = T2A(ipaddr.GetBuffer(0));

	list<DiscoverInfo> info;
	m_uiListOnvif.DeleteAllItems();
	Onvif_DiscoverAll(5, mip, _outonvifip, &info);

	list<DiscoverInfo>::iterator itr;
	for (itr = info.begin(); itr != info.end(); itr++) 
	{
		USES_CONVERSION;
		CString ip = A2T((*itr).ipaddr.c_str());
		CString xaddrs = A2T((*itr).probe.xaddrs);

		m_uiListOnvif.InsertItem(0, ip);
		m_uiListOnvif.SetItemText(0, 1, xaddrs);
	}
	ipaddr.ReleaseBuffer();
}


void OnvifInfoDialog::OnNMDblclkListOnvifDevice(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码

	int col = 0;
	int row = 0;
	row = pNMItemActivate->iItem;
	col = pNMItemActivate->iSubItem;

	CString xaddr = m_uiListOnvif.GetItemText(row, 1);

	SetDlgItemText(IDC_EDIT_DEVURI, xaddr);

	*pResult = 0;
}
